// Written by Dale J Williams.
// Licensed under MPL 1.1 unless otherwise specified. 
// http://www.mozilla.org/MPL/MPL-1.1.html

package engineDJW.sound {
	
	import engineDJW.GameCore;
	import flash.events.Event;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	
	/**
	 * SoundObject: Contains and controls a sound, channel.
	 * @author Dale J Williams
	 */
	public class SoundObject {
		
		protected var _manager		:SoundManager;
		protected var _track		:SoundTrack;
		protected var _name			:String;
		
		protected var _volume		:Number;
		protected var _volumeAbs	:Number;
		
		protected var _sound		:Sound;
		protected var _channel		:SoundChannel;
		protected var _transform	:SoundTransform;
		protected var _position		:Number;		
		protected var _playing		:Boolean;
		protected var _looping		:Boolean;
		protected var _initVolume	:Number;
		protected var _fadingOut	:Boolean;
		
		public function SoundObject($name:String, $urlOrSound:Object=null, $volume:Number=1, $looping:Boolean=false) {
			_name = $name;
			_volume = _volumeAbs = $volume;
			_looping = $looping;
			if ($urlOrSound) load($urlOrSound);
			init();
		}
		
		private function init():void {
			_channel = new SoundChannel();
			_transform = new SoundTransform();
		}
		
		public function load($urlOrSound:Object):void {
			if ($urlOrSound is String) {
				_sound = new Sound(new URLRequest(String($urlOrSound)));
			} else if ($urlOrSound is Sound) {
				_sound = Sound($urlOrSound);
			} else throw new Error("SoundObject must be loaded from Sound (in-built) or URL (String)");
		}
		
		public function play():void {
			if (!_sound) throw new Error("SoundObject not loaded.");
			if (_playing) close();
			doPlay();
			if (!_looping) _channel.addEventListener(Event.SOUND_COMPLETE, playComplete);
			updateVolume();
		}
		private function doPlay():void {
			_playing = true;
			var loops:int = _looping ? int.MAX_VALUE : 0;
			try {
				_channel = _sound.play(_position, loops);
			} catch(e:*) {}
		}
		private function playComplete(e:Event):void {
			_track.stopSound(this, true);
		}
		
		public function pause():void {
			_position = _channel.position;
			close();
		}
		
		public function stop():void {
			_position = 0;
			close();
		}
		
		public function cancelFade():void {
			if (!_fadingOut) return;
			GameCore.tween.kill(this);
			volume = _initVolume;
		}
		
		public function fadeOut(time:Number):void {
			trace("fadeOut");
			if (time > 0) {
				_initVolume = _volume;
				_fadingOut = true;
				GameCore.tween.to(this, time, { volume:0, onComplete:fadeOutComplete } );
			}
			else fadeOutComplete();
		}
		private function fadeOutComplete():void {
			stop();
			_track.stopSound(this, true);
			if(!isNaN(_initVolume)) {
				volume = _initVolume;
				_initVolume = NaN;
				_fadingOut = false;
			}
		}
		
		private function close():void {
			_playing = false;
			_channel.stop();
			try { 
				_sound.close(); 
			} catch (e:*) {};
		}
		
		public function rewind():void {
			_position = 0;
			if (_playing) play();
		}
		
		public function seekTo(milliseconds:Number):void {
			_position = milliseconds;
			if (_playing) play();
		}
		
		public function updateVolume():void {
			_volumeAbs = _volume * _track.volumeAbs;
			_transform.volume = _volumeAbs;
			if (_channel) _channel.soundTransform = _transform;
		}
		
		public function get volume():Number { return _volume; }		
		public function set volume(value:Number):void {
			_volume = value;
			updateVolume();
		}
		
		public function get volumeAbs():Number { return _volumeAbs; }
		
		public function get track():SoundTrack { return _track; }		
		public function set track(value:SoundTrack):void {
			_track = value;
			_manager = _track ? _track.manager : null;
			updateVolume();
		}
		
		public function get name():String { return _name; }
		
		public function get manager():SoundManager { return _manager; }
		
		public function get sound():Sound { return _sound; }
		
		public function get channel():SoundChannel { return _channel; }
		
		public function get transform():SoundTransform { return _transform; }
		
		public function get playing():Boolean { return _playing; }		
		
		public function get looping():Boolean { return _looping; }
		
	}

}